package org.kealinghornets.nxtdroneapp;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Fragment;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.PixelFormat;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;

import com.camera.simplemjpeg.MjpegInputStream;
import com.camera.simplemjpeg.MjpegView;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.kealinghornets.nxtdroid.NXT.NXT;
import org.kealinghornets.nxtdroid.NXT.NXTConnectionManager;
import org.kealinghornets.nxtdroneapp.bluetooth.BluetoothConnectActivityReceiver;
import org.kealinghornets.nxtdroneapp.bluetooth.BluetoothConnector;
import org.kealinghornets.nxtdroneapp.bluetooth.BluetoothDevicePicker;

import java.io.IOException;
import java.net.URI;

public class MainActivity extends Activity implements BluetoothConnector {

    BluetoothConnectActivityReceiver mBluetoothPickerReceiver;
    NXTConnectionManager connectionManager;
    DroneStatusThread droneStatusThread;

    DroneView status;
//    VideoView camera;
    MjpegView camera;
//    WebView camera;
    JoypadView leftpad;
    JoypadView rightpad;

    FakeControlThread fakeThread;
    NXT nxt;
    AlertDialog.Builder ip_address_input;
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        connectionManager = new NXTConnectionManager(this);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_main);

        mBluetoothPickerReceiver = new BluetoothConnectActivityReceiver(this);

        camera = (MjpegView)findViewById(R.id.drone_camera);

        status = (DroneView)findViewById(R.id.drone_status);

        status.getHolder().setFormat(PixelFormat.TRANSPARENT);

        droneStatusThread = new DroneStatusThread();
        droneStatusThread.status = status;

        droneStatusThread.leftPad = (JoypadView)findViewById(R.id.throttle_pad);
        droneStatusThread.rightPad = (JoypadView)findViewById(R.id.pitch_yaw_pad);


        leftpad = droneStatusThread.leftPad;
        rightpad = droneStatusThread.rightPad;

        droneStatusThread.setPriority(Thread.MAX_PRIORITY);

//            fakeThread = new FakeControlThread();
//            fakeThread.start();

        ((JoypadView)findViewById(R.id.throttle_pad)).lockXAxis = true;

/*
        if (savedInstanceState == null) {
            getFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();
        }
        */
    }


    public void showIPInputDialog() {
        View promptView =  LayoutInflater.from(this).inflate(R.layout.webcam_prompt, null);
        ip_address_input = new AlertDialog.Builder(this);
        ip_address_input.setView(promptView);
        final EditText ip_address = (EditText)promptView.findViewById(R.id.ip_address_edittext);
        ip_address.setText(getResources().getString(R.string.default_ip));
        ip_address_input.setCancelable(false);
        ip_address_input.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                startCamera(ip_address.getText().toString().trim());
            }
        });
        ip_address_input.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });
        ip_address_input.show();
    }

    public void startCamera(String ip) {
        new DoRead().execute("http://" + ip + ":8080/videofeed");
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.video_settings) {
            showIPInputDialog();
        }
        if (id == R.id.bluetooth_settings) {
            startActivity(new Intent(
                    android.provider.Settings.ACTION_BLUETOOTH_SETTINGS));
        }
        if (id == R.id.bluetooth_connect) {
            registerReceiver(mBluetoothPickerReceiver, new IntentFilter(
                    BluetoothDevicePicker.ACTION_DEVICE_SELECTED));
            startActivity(new Intent(BluetoothDevicePicker.ACTION_LAUNCH)
                    .putExtra(BluetoothDevicePicker.EXTRA_NEED_AUTH, false)
                    .putExtra(BluetoothDevicePicker.EXTRA_FILTER_TYPE,
                            BluetoothDevicePicker.FILTER_TYPE_ALL)
                    .setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS));
        }
        if (id == R.id.safety_kill_all) {
            killThreads();
        }
        return super.onOptionsItemSelected(item);
    }

    String TAG = "DroneView";

    public class DoRead extends AsyncTask<String, Void, MjpegInputStream> {
        protected MjpegInputStream doInBackground(String... url) {
            HttpResponse res = null;
            DefaultHttpClient httpclient = new DefaultHttpClient();
            try {
                res = httpclient.execute(new HttpGet(URI.create(url[0])));
                Log.d(TAG, "Request finished, status = " + res.getStatusLine().getStatusCode());
                if(res.getStatusLine().getStatusCode()==401){
                    //You must turn off camera User Access Control before this will work
                    return null;
                }
                Log.d(TAG, "Creating Mjpeginputstream");
                return new MjpegInputStream(res.getEntity().getContent());
            } catch (ClientProtocolException e) {
                e.printStackTrace();
                Log.d(TAG, "Request failed-ClientProtocolException", e);
                //Error connecting to camera
            } catch (IOException e) {
                e.printStackTrace();
                Log.d(TAG, "Request failed-IOException", e);
                //Error connecting to camera
            }

            return null;
        }

        protected void onPostExecute(MjpegInputStream result) {
            Log.d(TAG, "Post execute");
            camera.setSource(result);
            camera.setDisplayMode(MjpegView.SIZE_BEST_FIT);
        }
    }

    public void killThreads() {
        droneStatusThread.running = false;
        camera.mRun = false;
        boolean cameraKilled = false;
        while (!cameraKilled) {
            try {
                camera.thread.join();
                cameraKilled = true;
            } catch (Exception e) {
            }
        }
        boolean droneStatusKilled = false;
        while (!droneStatusKilled) {
            try {
                droneStatusThread.join();
                droneStatusKilled = true;
            } catch (Exception e) {
            }
        }
        Object killer = null;
        killer.toString();
    }

    public String getRealPathFromURI(Uri contentUri)
    {
        try
        {
            String[] proj = {MediaStore.Video.Media.DATA};
            Cursor cursor = managedQuery(contentUri, proj, null, null, null);
            int column_index = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
            cursor.moveToFirst();
            return cursor.getString(column_index);
        }
        catch (Exception e)
        {
            return contentUri.getPath();
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 1 && resultCode == RESULT_OK) {
            /*
            camera.setVideoPath(getRealPathFromURI(data.getData()));
            camera.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    camera.start();
                }
            });
            camera.start();
            */
        }
    }

    public void selectNXT() {
        registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (BluetoothDevicePicker.ACTION_DEVICE_SELECTED.equals(intent.getAction())) {
                    context.unregisterReceiver(this);
                    BluetoothDevice device = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    String name = device.getName();
                    String address = device.getAddress();
                    connectToService(name, address);
                }
            }
        }, new IntentFilter(BluetoothDevicePicker.ACTION_DEVICE_SELECTED));
    }

    public void connectToService(String name, String address) {
        nxt = connectionManager.connect(address);
        if (nxt == null) {
            status.connection = "CONNECTION FAILED";
            return;
        }
        droneStatusThread.setNXT(nxt);
        droneStatusThread.start();
        status.connection = name;
        registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (nxt.deviceAddress == intent.getStringExtra(NXTConnectionManager.LCP_DISCONNECT_DEVICEADDRESS)) {
                    context.unregisterReceiver(this);
                    notifyDisconnect();
                }
            }
        }, new IntentFilter(NXTConnectionManager.LCP_DISCONNECT));
    }

    public void notifyDisconnect() {
        status.connection = "DISCONNECTED";
        droneStatusThread.running = false;
    }

    public class FakeControlThread extends Thread {
        public void run() {
            while (true) {
                try {
                    sleep(10);
                } catch (Exception e) {

                }
                if (leftpad != null && rightpad != null) {
                    double throttle_input = leftpad.getJoypadY();
                    double yaw_input = rightpad.getJoypadX();
                    if (Math.abs(throttle_input) < 0.2) {
                        throttle_input = 0;
                    }
                    if (Math.abs(yaw_input) < 0.2) {
                        yaw_input = 0;
                    }
                    status.left_engine_output = (int)(throttle_input * 100 + yaw_input * 60);
                    status.right_engine_output = (int)(throttle_input * 100 - yaw_input * 60);

                    if (status.left_engine_output > 100) {
                        status.left_engine_output = 100;
                    }
                    if (status.left_engine_output < -100) {
                        status.left_engine_output = -100;
                    }
                    if (status.right_engine_output > 100) {
                        status.right_engine_output = 100;
                    }
                    if (status.right_engine_output < -100) {
                        status.right_engine_output = -100;
                    }

                    double bowplane_input = rightpad.getJoypadY();
                    if (Math.abs(bowplane_input) > 0.2) {
                        status.bow_plane -= bowplane_input;
                        if (status.bow_plane < -180) {
                            status.bow_plane = -180;
                        }
                        if (status.bow_plane > 180) {
                            status.bow_plane = 180;
                        }
                    }

                    double fake_range_input = leftpad.getJoypadY();
                    if (Math.abs(fake_range_input) > 0.2) {
                        status.range -= fake_range_input;
                        if (status.range > status.max_range) {
                            status.range = status.max_range;
                        }
                        if (status.range < 0) {
                            status.range = 0;
                        }
                    }
                    if (Math.abs(fake_range_input) > 0.2) {
                        status.altitude += fake_range_input;
                        if (status.altitude > status.max_altitude) {
                            status.altitude = status.max_altitude;
                        }
                        if (status.altitude < 0) {
                            status.altitude = 0;
                        }
                    }
                }
            }
        }
    }
}
